﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;

namespace EpiMapit
{
	public class CaseSetGroupBuilder
	{
        private GeoCodingResources geoCodingResources;
        private const char splitToken = '/';
        private Specification spec;
        

        public CaseSetGroupBuilder(GeoCodingResources geoCodingResources, Specification spec)
        {
            this.geoCodingResources = geoCodingResources;
            this.spec = spec;
        }

		public CaseSetGroup GenerateCaseSetGroupFromCSV(string input, DateTime date)
		{
            CaseSetGroup caseSetGroup = new CaseSetGroup();

			CSVReader csv = new CSVReader(input);

			string[] fields;

            // first read titles
            fields = csv.GetCSVLine();
            string[] titles = ReadTitles(fields);

			// read other lines
			fields = csv.GetCSVLine();

			while (fields != null && !Empty(fields))
			{
				CaseSet caseSet = CreateCaseSet(titles, fields, date);

                if (caseSet != null && !caseSet.Levels.All(e => e.Value == 0))
                {
                    caseSetGroup.Add(caseSet);    
                }

				fields = csv.GetCSVLine();
			}

			csv.Dispose();

            return caseSetGroup;
		}

        private bool Empty(string[] fields)
        {
            bool isEmpty = true;

            for (int i = 0; i < fields.Length && isEmpty; i++)
            {
                isEmpty = string.IsNullOrEmpty(fields[i]);
            }

            return isEmpty;
        }

        private string[] ReadTitles(string[] fields)
        {
            string[] titles = new string[fields.Length];

            int titleIndex = 0;
            foreach (string field in fields)
            {
                titles[titleIndex] = field;
                titleIndex++;
            }

            return titles;
        }

		private CaseSet CreateCaseSet(string[] titles, string[] fields, DateTime date)
		{
			CaseSet cs = new CaseSet();

            string country = fields[spec.Country];
            string region = fields[spec.Region];
            string countryTwoLetterISO = fields[spec.TwoLetterISO];
            string GEOCode = fields[spec.GEOCode];

            string regionCode = GetRegionCode(country, region);
            Position pos = new Position();
            bool isGeoCoded = GeoCode(country, region, countryTwoLetterISO, GEOCode, out pos);

            if (!isGeoCoded)
            {
                string msg = string.Format("Cannot find coord for: Rg: {0}, Ct: {1}, Cd: {2}, Is: {3}", 
                                region ?? "", country ?? "",
                                GEOCode ?? "", countryTwoLetterISO ?? "");

                return null;
                //throw new Exception(msg);
            }

            cs.Lat = pos.Latitude;
            cs.Lon = pos.Longitude;
			cs.RegionCode = regionCode;

            cs.HasDate = date != null;
            cs.Date = date;

            foreach (var entry in spec.Cases)
            {
                string value = fields[entry.Value];
                int intValue = string.IsNullOrEmpty(value) ? 0 : int.Parse(value);
                cs.Levels.Add(titles[entry.Value], intValue);
            }

			return cs;
		}

        private void SplitDate(string date, out int year, out int month, out int day)
        {
            string[] date_array = date.Split(splitToken);
            day = int.Parse(date_array[0]);
            month = int.Parse(date_array[1]);
            year = int.Parse(date_array[2]);
        }

        private bool GeoCode(string country, string region, string countryLb, string geoCode, out Position pos)
        {
            pos = new Position();;
            bool gotIt = false;

            if (!string.IsNullOrEmpty(geoCode))
            {
                gotIt = geoCodingResources.byGEOCode.TryGetValue(geoCode, out pos);
                if (gotIt) return true;
            }

            string geoCodeCut = Auxiliars.CutEndingZeros(geoCode);

            if (!string.IsNullOrEmpty(geoCodeCut))
            {
                gotIt = geoCodingResources.byGEOCodeNoEndZeros.TryGetValue(geoCodeCut, out pos);
                if (gotIt) return true;
            }

            if (!string.IsNullOrEmpty(region))
            {
                gotIt = geoCodingResources.byRegion.TryGetValue(region, out pos);
                if (gotIt) return true;
            }

            if (!string.IsNullOrEmpty(region))
            {
                gotIt = geoCodingResources.byRegion.TryGetValue(region, out pos);
                if (gotIt) return true;
            }

            /*
             * IF THIS PART IS REACHED, THE LATITUDE AND LONGITUDE RETURNED
             * WON'T BE THE EXACT ONES FOR THE REGION BUT FOR THE COUNTRY
             */
            if (!string.IsNullOrEmpty(countryLb))
            {
                gotIt = geoCodingResources.byCountryISO2.TryGetValue(countryLb, out pos);
                if (gotIt) return true;
            }

            if (!string.IsNullOrEmpty(country))
            {
                gotIt = geoCodingResources.byCountry.TryGetValue(country, out pos);
                if (gotIt) return true;
            }

            return false;
        }

        private string GetRegionCode(string country, string region)
        {
            string regionId = string.Empty;

            if (string.IsNullOrEmpty(country))
            {
                regionId = region;
            }
            else if (string.IsNullOrEmpty(region))
            {
                regionId = country;
            }
            else
            {
                regionId = string.Format("{0} - {1}", region, country);
            }

            return regionId;
        }

        internal CaseSetGroup GenerateCaseSetGroupFromDir(string inputDir)
        {
            DirectoryInfo di = new DirectoryInfo(inputDir);
            FileInfo[] rgFiles = di.GetFiles("*.csv");

            List<CaseSetGroup> csgList = new List<CaseSetGroup>();

            foreach (FileInfo fi in rgFiles)
            {
                // parse date
                DateTime date = Auxiliars.ParseDate(fi.Name);

                CaseSetGroup csg = GenerateCaseSetGroupFromCSV(fi.FullName, date);
                csgList.Add(csg);
            }

            CaseSetGroup ret = FlattenCaseSetGroupList(csgList);

            return ret;
        }

        private CaseSetGroup FlattenCaseSetGroupList(List<CaseSetGroup> csgList)
        {
            List<CaseSet> list = new List<CaseSet>();

            foreach (var csg in csgList)
            {
                list.AddRange(csg);
            }

            return new CaseSetGroup(list);
        }
    }
}
